﻿using UnityEngine;
using System.Collections;
using System;
using UObject = UnityEngine.Object;
using UnityEditor;
using System.Collections.Generic;
using System.IO;

namespace ZL_Framework
{
    /// <summary>
    /// AB包资源管理器
    /// </summary>
    [StartManagerAttribute]
    public class AssetBundlesManager : MonoSingleton<AssetBundlesManager>
    {
        /*
         * 使用：
         * AssetBundlesManager.Instance.LoadAsset<GameObject>("", "Assets/AssetBundles/Game/dye_point.prefab", (obj)=> { GameObject go = GameObject.Instantiate(obj) as GameObject; });
         * 
         */

        private AssetBundleManifest manifest = null;
        private string m_assetbundleExtension = ".assetbundle";
        private IDictionary<string, AssetBundle> m_assetbundle_dic = new Dictionary<string, AssetBundle>();
        private string AssetbundlePath;
        private string AssetbundleStrmingPath;

        protected override void Init()
        {
            LuaFile = null;
            LoadAssetBundleManifest();
        }

        /// <summary>
        /// 加载AB包依赖文件
        /// </summary>
        public void LoadAssetBundleManifest()
        {
            if (!Application.isEditor || AppConfig.Instance.Config.PlayMode != PlayMode.SimulateLoad)
            {
                AssetbundlePath = Path.Combine(AppConfig.Instance.ABPath, "assetbundle");
                AssetBundle bundle = null;
                if (File.Exists(Path.Combine(AssetbundlePath, "assetbundle")))
                {
                    bundle = AssetBundle.LoadFromFile(Path.Combine(AssetbundlePath, "assetbundle"));
                }
                else
                {
                    AssetbundleStrmingPath = Path.Combine(Path.Combine(Application.streamingAssetsPath, "serverassets"), "assetbundle");
                    bundle = AssetBundle.LoadFromFile(Path.Combine(AssetbundleStrmingPath, "assetbundle"));
                }
                manifest = bundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                // 压缩包释放掉
                bundle.Unload(false);
            }
        }

        /// <summary>
        /// 加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="bundleName">ab包名</param>
        /// <param name="assetName">资源名（Assets下完整路径， 带文件格式后缀）</param>
        /// <param name="loaded"></param>
        /// <param name="isAB"></param>
        public void LoadAsset(string bundleName, string assetName, Action<UObject> loaded, bool isAB = true)
        {
            StartCoroutine(Loaded(bundleName, assetName, (obj) =>
              {
                  loaded?.Invoke(obj);
              }, isAB));
        }

        private IEnumerator Loaded(string bundleName, string assetName, Action<UObject> loaded, bool isAB)
        {
            LogTool.Instance.Log("[开始资源加载] " + assetName);
            if (Application.isEditor && AppConfig.Instance.Config.PlayMode == PlayMode.SimulateLoad)
            {
#if UNITY_EDITOR
                UObject objPrefab = AssetDatabase.LoadAssetAtPath<UObject>(assetName);
                if (objPrefab != null)
                {
                    loaded?.Invoke(objPrefab);
                    LogTool.Instance.Log("[资源加载成功] " + assetName);
                }
                else
                {
                    LogTool.Instance.LogError("[资源加载失败] " + assetName);
                }
#endif
            }
            else
            {
                if (!isAB)
                {
                    ResourceRequest loadedAsset = null;

                    if (assetName.Contains("Assets/Resources/"))
                    {
                        assetName = assetName.Replace("Assets/Resources/", "");
                    }
                    if (assetName.Contains("."))
                    {
                        string[] names = assetName.Split('.');
                        assetName = names[0];
                    }
                    loadedAsset = Resources.LoadAsync(assetName);

                    if (loadedAsset != null)
                    {
                        yield return loadedAsset;

                        loaded?.Invoke(loadedAsset.asset);
                        LogTool.Instance.Log("[Resource资源加载成功] " + assetName);
                    }
                    else
                    {
                        LogTool.Instance.LogError("[Resource资源加载失败] " + assetName);
                    }
                }
                else
                {
                    if (!bundleName.Contains(m_assetbundleExtension))
                    {
                        bundleName = bundleName + m_assetbundleExtension;
                    }

                    AssetBundleCreateRequest request = null;

                    bundleName = bundleName.ToLower();
                    bundleName = bundleName.Replace('/', '-');
                    bundleName = bundleName.Replace(" ", "(_)");

                    List<AssetBundle> dependencies = new List<AssetBundle>();
                    if (manifest == null)
                    {
                        yield break;
                    }
                    string[] dependence = manifest.GetAllDependencies(bundleName);

                    for (int i = 0; i < dependence.Length; ++i)
                    {
                        if (!m_assetbundle_dic.ContainsKey(dependence[i]))
                        {
                            string path = Path.Combine(AssetbundlePath, dependence[i]);
                            if (!File.Exists(path))
                            {
                                path = Path.Combine(AssetbundleStrmingPath, dependence[i]);
                            }
                            request = AssetBundle.LoadFromFileAsync(path);

                            if (request != null)
                            {
                                yield return request;
                                if (request.isDone)
                                {
                                    if (request.assetBundle != null)
                                    {
                                        dependencies.Add(request.assetBundle);
                                        m_assetbundle_dic.Add(request.assetBundle.name, request.assetBundle);
                                        LogTool.Instance.Log("[AB包依赖加载成功] " + request.assetBundle.name);
                                    }
                                    else
                                    {
                                        LogTool.Instance.LogError("[AB包依赖加载失败] " + dependence[i]);
                                        yield break;
                                    }
                                }
                            }
                        }
                    }

                    AssetBundle bundle = null;
                    AssetBundleRequest loadedAsset = null;
                    if (!m_assetbundle_dic.ContainsKey(bundleName))
                    {
                        string path = Path.Combine(AssetbundlePath, bundleName);
                        if (!File.Exists(path))
                        {
                            path = Path.Combine(AssetbundleStrmingPath, bundleName);
                        }
                        request = AssetBundle.LoadFromFileAsync(path);

                        if (request != null)
                        {
                            yield return request;
                            if (request.isDone)
                            {
                                bundle = request.assetBundle;
                            }
                            if (bundle == null)
                            {
                                yield break;
                            }

                            loadedAsset = bundle.LoadAssetAsync(assetName);
                            if (loadedAsset != null)
                            {
                                yield return loadedAsset;
                                m_assetbundle_dic.Add(bundle.name, bundle);
                            }
                        }
                    }
                    else
                    {
                        bundle = m_assetbundle_dic[bundleName];
                        loadedAsset = bundle.LoadAssetAsync(assetName);

                        if (loadedAsset != null)
                        {
                            yield return loadedAsset;
                        }
                    }

                    if (loadedAsset != null)
                    {
                        loaded?.Invoke(loadedAsset.asset);
                        LogTool.Instance.Log("[AB包资源加载成功] " + assetName);
                    }
                    else
                    {
                        LogTool.Instance.LogError("[AB包资源加载失败] " + assetName);
                        yield break;
                    }
                }
            }
            yield return 0;
        }

        public LuaSerializeFile LuaFile
        {
            get;
            private set;
        }


        /// <summary>
        /// 加载Lua
        /// </summary>
        /// <param name="action"></param>
        public void LoadLua(Action action)
        {
            StartCoroutine(LoadLuaSerializeFile(action));
        }

        private IEnumerator LoadLuaSerializeFile(Action action)
        {
            if (!(AppConfig.Instance.Config.PlayMode == PlayMode.SimulateLoad && Application.isEditor))
            {
                AssetBundleCreateRequest fileRequest = AssetBundle.LoadFromFileAsync(AppConfig.Instance.LuaFilePath + "lua.assetbundle");
                yield return fileRequest;

                if (fileRequest != null)
                {
                    AssetBundle ab = fileRequest.assetBundle;
                    AssetBundleRequest assetRequest = ab.LoadAssetAsync("Assets/lua.asset");
                    yield return assetRequest;

                    LuaFile = assetRequest.asset as LuaSerializeFile;
                    LuaFile.ConvertFileMap();

                }
            }
            LogTool.Instance.Log("Lua Loaded");
            action?.Invoke();
        }

        /// <summary>
        /// 加载GameObject资源
        /// </summary>
        /// <param name="bundleName">ab包名</param>
        /// <param name="assetName">资源名（Assets下完整路径， 带文件格式后缀）</param>
        /// <param name="loaded"></param>
        /// <param name="isAB"></param>
        public void LoadInstantiateGameObject(string bundleName, string assetName, Action<string, GameObject> loaded, bool isAB = true)
        {
            LoadAsset(bundleName, assetName, (obj) =>
            {
                if (obj != null)
                {
                    GameObject go = GameObject.Instantiate(obj) as GameObject;
                    loaded?.Invoke(assetName, go);
                }
            }, isAB);
        }

        /// <summary>
        /// 加载音频文件
        /// </summary>
        /// <param name="bundleName">ab包</param>
        /// <param name="assetName">资源名（Assets下完整路径， 带文件格式后缀）</param>
        /// <param name="loaded"></param>
        /// <param name="isAB"></param>
        public void LoadAudioClip(string bundleName, string assetName, Action<string, AudioClip> loaded, bool isAB = true)
        {
            LoadAsset(bundleName, assetName, (aud) =>
            {
                if (aud != null)
                {
                    loaded?.Invoke(assetName, aud as AudioClip);
                }
            }, isAB);
        }

        /// <summary>
        /// 加载Texture2D
        /// </summary>
        /// <param name="bundleName">ab包名</param>
        /// <param name="assetName">资源名（Assets下完整路径， 带文件格式后缀）</param>
        /// <param name="loaded"></param>
        /// <param name="isAB"></param>
        public void LoadTexture2D(string bundleName, string assetName, Action<string, Texture2D> loaded, bool isAB = true)
        {
            LoadAsset(bundleName, assetName, (tex) =>
            {
                if (tex != null)
                {
                    loaded?.Invoke(assetName, tex as Texture2D);
                }
            }, isAB);
        }

        /// <summary>
        /// 加载Sprite
        /// </summary>
        /// <param name="bundleName">ab包名</param>
        /// <param name="assetName">资源名（Assets下完整路径， 带文件格式后缀）</param>
        /// <param name="loaded"></param>
        /// <param name="pivot">sprite锚点</param>
        /// <param name="isAB"></param>
        public void LoadSprite(string bundleName, string assetName, Action<string, Sprite> loaded, Vector2 pivot = default, bool isAB = true)
        {
            LoadTexture2D(bundleName, assetName, (str, tex) =>
            {
                if (tex != null)
                {
                    Sprite sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), pivot);
                    loaded?.Invoke(assetName, sprite);
                }
            }, isAB);
        }

        /// <summary>
        /// 销毁所有资源
        /// </summary>
        public void UnLoadAllAssetbundle()
        {
            foreach (AssetBundle assetbundle in m_assetbundle_dic.Values)
            {
                LogTool.Instance.Log("UnLoadAssetbundle name is :" + assetbundle.name);
                assetbundle.Unload(true);
            }
            m_assetbundle_dic.Clear();
        }

        /// <summary>
        /// 卸载Assetbundle及关联的bundle
        /// </summary>
        /// <param name="bundleName">名称</param>
        /// <param name="unloadAsset">是否卸载创建的资源</param>
        public void UnLoadAssetbundleByName(string bundleName, bool unloadAsset)
        {
            if (AppConfig.Instance.Config.PlayMode == PlayMode.SimulateLoad && Application.isEditor)
            {
                return;
            }
            bundleName = bundleName.ToLower();
            bundleName = bundleName.Replace('/', '-');
            bundleName = bundleName.Replace(" ", "(_)");
            if (!bundleName.Contains(".assetbundle"))
            {
                bundleName += ".assetbundle";
            }
            if (m_assetbundle_dic.ContainsKey(bundleName))
            {
                m_assetbundle_dic[bundleName].Unload(unloadAsset);
                m_assetbundle_dic.Remove(bundleName);
                LogTool.Instance.Log("UnLoadAssetbundleByName - bundleName is :" + bundleName);
            }
        }
    }
}